#include "../core/integrator.h"

#include "../core/camera.h"
#include "../core/scene.h"

#include "../core/bsdf.h"
#include "../core/shape.h"
#include "../core/material.h"

#include <iostream>

namespace csupbr {

    Spectrum Li(Ray& ray, Intersection &isct) {
        RNG rng(rand());
        auto& bsdf = isct.primitive->material->bsdf;
        auto& shader = isct.primitive->material->shader;
        BSDFRecord rec(isct);
        rec.wo = -ray.direct;
        float pdf;
        Point2f rd = {rng.uniformFloat(), rng.uniformFloat()};
        auto f = bsdf->sample(rec,
                     rd,
                     pdf);
        isct.newRay(ray, rec.wi);
        return shader->shading(isct) * f / pdf;
    }

    void Whitted::render(RenderData &rd) const {
        RNG rng(rand());
        for (int h = rd.b - 1; h >= rd.t; h--) {
            for (int w = rd.l; w < rd.r; w++) {
                int count = 0;
                Spectrum col{.0f};
                while (count < spp && rd.is_rendering) {
                    Spectrum frag{1.f};
                    Vec2f uv = {(float)(w + rng.uniformFloat()) / rd.film->width,
                                (float)(h + rng.uniformFloat()) / rd.film->height};
                    uv = clamp(uv, Vec2f{.0f}, Vec2f{1.f});
                    CameraSample cs(uv);
                    auto ray = scene->camera->genRay(cs);
                    int depth = 0;
                    while (true) {
                        if (depth >= max_depth) {
                            frag *= 0.f;
                            break;
                        }
                        Intersection isct;
                        if (scene->intersect(ray, isct)) {
                            if (isct.primitive->material->bsdf) {
                                auto li = Li(ray, isct);
                                    frag *= li;
                            }
                            else {
                                frag *= isct.primitive->material->shader->shading(isct);
                                break;
                            }
                        } else {
                            frag *= scene->skybox ? scene->skybox->getSky(ray.direct, {rng.uniformFloat(),
                                                                                       rng.uniformFloat()})
                                                  : .0f;
                            break;
                        }
                        depth++;
                    }
                    if (ill_type == IlluminationType::DIRECT) {
                        if(depth < 2)
                            col += frag;
                    }
                    else if (ill_type == IlluminationType::INDIRECT) {
                        if (depth >= 2)
                            col += frag;
                    } else {
                        col += frag;
                    }
                    count++;
                }
                col /= (float)spp;
                if (is_gamma) {
                    col.x = std::pow(col.x, 1.f / 2.2f);
                    col.y = std::pow(col.y, 1.f / 2.2f);
                    col.z = std::pow(col.z, 1.f / 2.2f);
                }
                col = clamp(col, Spectrum{.0f}, Spectrum{1.f});
                rd.film->set_pixel(w, h, col);
            }
        }
    }

}